#ifndef LOS_H_
#define LOS_H_

/** @author Massimo Bono
 *  @date 6 May 2013
 *  @brief implementa il sottostrato LOS del livello ALG
 *
 *	Implementa il sottostrato LOS del livello ALG. il livello LOS viene richiamato dal livello STM in modo "on demand": ciò significa che
 *	viene chiamato quando il livello superiore ha bisogno di una nuova soluzione. LOS si occupa di calcolare una nuova soluzione da consegnare
 *	al STM. Come può calcolarla? A livello generale LOS genera un intorno della soluzione passata dal STM e, cercando di soddisfare i vincoli contenuti
 *	in una tabuPipe, calcola la migliore soluzione possibile diversa da quella iniziale.</br>
 *	Come genera l'intorno dipende: fondamentalmente ci sono 3 modi per generare un intorno:
 *		\li eseguendo una mossa ADD
 *		\li eseguendo una mossa SWAP
 *		\li eseguendo una mossa REMOVE
 *
 */

#include <stdbool.h>
#include "graph.h"
#include "solution.h"
#include "tabooState.h"
#include "solution.h"
#include "ALGConfiguration.h"

/**indica l'identificatore assegnato al tipo di mossa <b>ADD</b>. Questo identificartore viene usato da STM per comunicare a LOS quale
 * mossa ha scelto come generatrice di intorno
 */
#define MOVE_ADD 0
/**indica l'identificatore assegnato al tipo di mossa <b>SWAP</b>. Questo identificartore viene usato da STM per comunicare a LOS quale
 * mossa ha scelto come generatrice di intorno
 */
#define MOVE_SWAP 1
/**indica l'identificatore assegnato al tipo di mossa <b>REMOVE</b>. Questo identificartore viene usato da STM per comunicare a LOS quale
 * mossa ha scelto come generatrice di intorno
 */
#define MOVE_REMOVE 2

/** flag di uscita di una mossa LOS per indicare che ha avuto successo e la soluzione è migliorata. */
#define MOVE_STATUS_IMPROVE 1
/** flag di uscita di una mossa LOS per indicare che ha avuto successo e la soluzione è peggiorata. */
#define MOVE_STATUS_WORSEN 2
/** flag di uscita di una mossa LOS per indicare che ha avuto successo e la soluzione ha profitto identico.
TODO volendo inserirla anche in ADD e SWAP. è più corretta siccome molte operazioni che agiscono su archi dove la raccolta non è in discussione
 	 (perchè non profittevoli o non raccoglibili) NON cambiano il profit.*/
#define MOVE_STATUS_UNCHANGED 3
/** flag di uscita di una mossa LOS per indicare che è fallita e la soluzione non è stata modificata. */
#define MOVE_STATUS_FAIL 0

/** Struttura contenente tutte e sole le configurazioni di cui LOS necessita per girare. */
typedef struct LOSConfiguration {
	/**indica che valutazione usare nelle mosse tra PURE, SMART e WISE. si faccia riferimento ai valori delle macro */
	int evaluate;
	/**indica la tenure da associare ad un arco che entra in soluzione*/
	int addEdgeTenure;
	/**indica la tenure da associare ad un arco che esce dalla soluzione*/
	int removeEdgeTenure;
} LOSConfiguration;

typedef LOSConfiguration* pLOSConfiguration;

pLOSConfiguration initLOSConfiguration(PalgConfiguration pALGconfig);

/** Struttura utilizzata per le triangolazioni. */
typedef struct EdgePair {
	EdgeID e_il;
	EdgeID e_jl;
} EdgePair;
/** Puntatore di servizio. */
typedef EdgePair* PedgePair;

/** rappresenta la mossa migliore trovata da ADD fino a questo momento. Add infatti genera un suo particolare intorno e quindi
 * effettua un ciclo per ogni elemento dell'intorno. Quando viene trovata una mossa (temporanea) che migliora la soluzione
 * passata, ADD inserisce i parametri di tale mossa in una struttura di questo tipo. In questo modo quando ADD termina l'esplorazione
 * dell'intorno applica la mossa specificata da questa struttura.
 * */
typedef struct BestAddRemoveChoice {
	/** Archi cache contenenti il migliore candidato dell'AddNeighbourhood. */
	EdgeID e_il;
	/** (solo per ADD) Se al termine del neighbourhood punta anch'essa a e_il, allora la mossa effettuata è arco doppio. */
	EdgeID e_jl;
	EdgeID e_ij;
	/** Valore cache di confronto per raggiungere il best improvement. Più alto è, meglio è. */
	Weight bestScore;
} BestAddRemoveChoice;

/** puntatore di servizio.*/
typedef BestAddRemoveChoice* PbestAddRemoveChoice;

/**calcola la nuova soluzione cercando di soddisfare i vincoli taboo utilizzando come mossa per la generazione dell'intorno
 * quella passata da STM.
 *
 * \pre
 *  \li pg non NULL
 *  \li ps non NULL
 *  \li k compreso tra 0 e K
 *  \li ptl non NULL
 *  \li move ha uno dei possiibli valori MOVE_ADD, MOVE_SWAP o MOVE_REMOVE
 *  \li e' gia' stata richiamata la funzione initSmartWeights()
 *
 * @param graph indica il grafo su cui cercare di trovare una soluzione
 * @param solution indica la soluzione corrente
 * @param k indice del veicolo su cui STM vuole che ci si focalizzi
 * @param tabooState lo stato delle mosse vietate
 * @param moveType indica il tipo di ptaboostate->tls mossa che genera l'intorno
 * @param evaluateMode flag di selezione tra i criteri di valutazione (sostituisce vecchio sistema di macros)
 * @return un flag di stato sull'avvenuta o meno effettuazione di una mossa LOS. Puo' assumere 3 diversi valori:
 * 	\li MOVE_STATUS_IMPROVE se la ricerca locale ha trovato una nuova soluzione che migliora la soluzione passata;
 * 	\li MOVE_STATUS_WORSEN se la ricerca locale ha trovato una nuova soluzione che peggiora la soluzione passata;
 * 	\li MOVE_STATUS_FAIL se la ricerca locale ha fallito a trovare una nuova soluzione;
 */
int solveLocalSearch(Pgraph p_graph, Psolution p_solution, int k, PtabooState p_tabooState, int moveType, TabooExpire now, pLOSConfiguration pLOSconfig);

/** genera tutte le (N-2) triangolazioni possibili su un dato edge. L'edge passato puo' anche non essere validato
 *
 * \post
 * 	\li gli archi generati sono tutti validati;
 *
 * @param e_ij appartenente al grafo
 * @param N il numero totale di nodi presente nel grafo
 * @return N-2 strutture EdgePair (e_il, e_jl) tali che l \f$ \in [0,N-1] \setminus \{i, l\} \f$.
 */
PedgePair getEdgeTriangles(EdgeID e_ij, int N);

/**determina l'arco congiungente due archi consecutivi. Il contr
 *
 * \pre
 *  \li pe_il e pe_jl non NULL;
 * \post
 *  \li una nuova istanza (da deallocare se non piu' utile) di EdgeID;
 *  \li la nuova istanza e' validata
 *
 * @param e_il arco interno sx
 * @param e_jl arco interno dx
 * @return e_ij il terzo arco connettente i nodi estremi dei due archi interni
 */
PedgeID getEdgeTriangulating(PedgeID pe_il, PedgeID pe_jl);

#endif /* LOS_H_ */
